/**
 * @file   Vector.h
 * @author Shao <student@student>
 * @date   Tue Oct 19 20:47:21 2021
 * 
 * @brief  
 * 
 * 
 */
#include <algorithm>

template <typename Object>
class Vector
{
  public:
    /** 
     * @param initSize the initialized size of vector, the default size is zero, and the default capacity is initial size add the constant spare capacity. 
     * @return 
     */
    explicit Vector( int initSize = 0 ) : theSize { initSize } ,
         theCapacity { initSize + SPARE_CAPACITY }
      { objects = new Object[ theCapacity ]; }

     Vector( const Vector & rhs ) : theSize{ rhs.theSize },
    	 theCapacity{ rhs.theCapacity }, objects{ nullptr }
     {
         objects = new Object[ theCapacity ];
         for( int k = 0; k < theSize;k++ )
                objects[ k ] = rhs.objects[ k ];
     }
     
     /** 
      * 
      * @brief Copy assignment
      * @param rhs 
      * @return Vector&
      */
     Vector & operator= ( const Vector & rhs )
     {
         Vector copy = rhs;
         std::swap( *this,copy );
         return *this;
     }

     /** 
      * @brief Destroy the Vector object, relese the objects' memory.
      * 
      */
     ~Vector()
     { delete [ ] objects; }

     /** 
      * @brief  Move constructor
      * 
      * @param rhs 
      */
     Vector( Vector && rhs ):theSize{ rhs.theSize } ,
	 theCapacity{ rhs.theCapacity } , objects{ rhs.objects }
     {
         rhs.objects = nullptr;
         rhs.theSize = 0;
         rhs.theCapacity = 0;
     }

     /** 
      * @brief  Move assignment
      * @param rhs 
      * @return 
      */
     Vector & operator= ( Vector &&rhs )
     {
         std::swap( theSize,rhs.theSize );
         std::swap( theCapacity,rhs.theCapacity );
         std::swap( objects,rhs.opjects );
	 
         return *this;
     }

     /** 
      * @brief  Change the value of size. If the new size is larger than the capacity, call the reverse function to allocate new memory to expand capacity.
      * @param newSize 
      */
     void resize( int newSize )
     {
         if( newSize > theCapacity )
             reserve( newSize * 2 );
         theSize = newSize;
     }

     /** 
      * @brief  If the new capacity is smaller than the size, skip. Else allocate new memory space and then copy the original data.
      * @param newCapacity 
      */
     void reserve( int newCapacity )
     {
         if( newCapacity < theSize )
             return;
	 
         Object *newArray = new Object[ newCapacity ];
         for( int k = 0 ; k < theSize ; k++ )
             newArray[ k ] = std::move( objects[ k ] );
            
         theCapacity = newCapacity;
         std::swap( objects, newArray );
         delete [ ] newArray;
     }

     /** 
      * @brief return the element given a position.
      * @param index 
      * @return 
      */
     Object & operator[]( int index )
       { return objects[ index ]; }
     const Object &  operator[]( int index ) const
       { return objects[index]; }

     /** 
      * @brief Returns true if the container does not contain elements, otherwise returns false.
      * @return true if the vector is empty, i.e. the size is 0.
      */
     bool empty() const
       { return size() == 0; }
     
     /** 
      * @brief  return the size of vectorReturns the number of elements in the container.
      * @return int
      */
     int size() const
       { return theSize; }

     /** 
      * @brief return the capacity of vector.
      * @return int
      */
     int capacity() const
       { return theCapacity; }

     /** 
      * @brief  add a new element in vector. If the capacity is not enough, call the reserve function to allocate new memory space.
      * @param x 
      */
     void push_back( const Object & x )
     {
         if( theSize == theCapacity )
             reserve( 2 * theCapacity + 1 );
         objects[ theSize++ ] = x;
     }

     void push_back( Object && x )
     {
         if( theSize == theCapacity )
             reserve( 2 * theCapacity + 1 );
         objects[ theSize++ ] = std::move( x );
     }
     
     /** 
      * @brief Delete the last element just to minus the size.
      * 
      */
     void pop_back()
     {
         --theSize;
     }

     const Object & back () const
     {
          return objects[ theSize - 1 ];
     }

     typedef Object * iterator;              // pointor to an element whose type is Object.
     typedef const Object * const_iterator;  // the constant pointor.

     /** 
      * @brief return the first element in the vector.
      * @return 
      */
     iterator begin()
        { return &objects[ 0 ]; }
     const_iterator begin() const
        { return &objects[ 0 ]; }

     /** 
      * @brief  return the last element in the vector
      * @return 
      */
     iterator end()
        { return &objects[ size() ]; }
     const_iterator end() const
        { return &objects[ size() ]; }

     //static const int SPARE_CAPACITY = 16;     //Initial reserved capacity.

     
   private:
     int theSize;        // the number of the elements in the vector
     int theCapacity;    // the number of the allocated memory, which should be larger or equal to the size
     Object * objects;   // the objects(elements) in the vector
};
